package com.ch.tools.client;

import com.ch.ms.server.base.MaplePacketDecoder;
import com.ch.ms.server.base.MaplePacketEncoder;
import com.ch.ms.server.packet.*;
import com.ch.tools.client.simulator.LoginLauncher;
import com.ch.tools.client.simulator.observable.Observables;
import com.ch.tools.client.simulator.view.CharacterListView;
import com.ch.tools.client.simulator.view.ServerListView;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Objects;
import java.util.UUID;


@Component
@Scope("singleton")
@Data
@Slf4j
@Builder
@AllArgsConstructor
public class MapleClient {

    private String id = UUID.randomUUID().toString();


    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private SocketChannel socketChannel;


    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private Bootstrap bootstrap;


    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private String host;

    @Getter(AccessLevel.NONE)
    @Setter(AccessLevel.NONE)
    private int port;


    public MapleClient() {
        log.info("创建客户端 {}", id);
        EventLoopGroup eventLoopGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.channel(NioSocketChannel.class);
        bootstrap.option(ChannelOption.SO_KEEPALIVE, true);
        bootstrap.group(eventLoopGroup);
        bootstrap.handler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                //心跳检测,如果10秒内没有发送任何报文,则调用ChannelHandlerContext.fireUserEventTriggered来触发IdleStateEvent事件并交给下一个handler来处理
                socketChannel.pipeline().addLast(new IdleStateHandler(0, 60, 0));
                socketChannel.pipeline().addLast(new LoggingHandler(LogLevel.INFO));
                socketChannel.pipeline().addLast(new MaplePacketDecoder(new HashMap<MaplePacketHeader, Class<? extends MaplePacket>>() {
                    private static final long serialVersionUID = -1271491567671931523L;

                    {
                        put(MaplePacketHeader.PONG, PongPacket.class);
                        put(MaplePacketHeader.LOGIN_RES, LoginResultPacket.class);
                        put(MaplePacketHeader.LOAD_CHARACTERS_RES, LoadCharactersResPacket.class);
                    }
                }));
                socketChannel.pipeline().addLast(new MaplePacketEncoder());
                socketChannel.pipeline().addLast(new SimpleChannelInboundHandler<MaplePacket>() {
                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, MaplePacket maplePacket) throws Exception {
                        log.info("客户端接收到消息 {}", maplePacket);


                        if (maplePacket instanceof LoginResultPacket) {

                            if (((LoginResultPacket) maplePacket).isSuccess()) {
                                Platform.runLater(() -> {
                                    LoginLauncher.showView(ServerListView.class);
                                    Observables.getMapleWorldServerPacketObservable().setMapleServers(((LoginResultPacket) maplePacket).getWorldServers());
                                });

                            } else {
                                Platform.runLater(() -> {
                                    Alert alert = new Alert(Alert.AlertType.ERROR);
                                    alert.setTitle("错误");
                                    alert.setContentText("登录失败!");
                                    alert.showAndWait();
                                });
                            }
                        }


                        if (maplePacket instanceof LoadCharactersResPacket) {

                            Platform.runLater(() -> {
                                LoginLauncher.showView(CharacterListView.class);
                                Observables.getMapleCharactersObservable().setCharacters(((LoadCharactersResPacket) maplePacket).getCharacters());
                            });
                        }

                    }

                    @Override
                    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                        super.userEventTriggered(ctx, evt);

                        if (evt instanceof IdleStateEvent) {
                            IdleStateEvent idleStateEvent = (IdleStateEvent) evt;
                            if (idleStateEvent.state() == IdleState.WRITER_IDLE) {
                                //向服务端发送消息
                                //ctx.write(socketChannel.id().asLongText());
                                // send(socketChannel.id().asLongText());
                                //ctx.writeAndFlush(socketChannel.id().asLongText()).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
//                                log.info("channel is open {}", socketChannel.isOpen());
//                                log.info("channel is writeable {}", socketChannel.isWritable());
//                                log.info("channel is active {}", socketChannel.isActive());
//                                log.info("channel is shutdown {}", socketChannel.isShutdown());
//                                socketChannel.writeAndFlush("ping");

                                send(new PingPacket());
                            }


                        }
                    }
                });

                socketChannel.pipeline().addLast(new StringEncoder());  //对报文进行编码
            }
        });
    }

    public boolean connect(String host, int port) throws InterruptedException {
        this.host = host;
        this.port = port;
        if (Objects.nonNull(socketChannel)) socketChannel.eventLoop().shutdownGracefully();

        ChannelFuture channelFuture = bootstrap.connect(host, port).sync();
        boolean result = false;
        if (result = channelFuture.isSuccess()) {
            socketChannel = (SocketChannel) channelFuture.channel();
            log.info("连接到服务器{}:{}成功", host, port);
        }
        return result;
    }

    public void send(MaplePacket maplePacket) {
        log.info("通道[{}]发送报文:{}", this.socketChannel.id(), maplePacket);
        try {
            if (!this.socketChannel.isWritable())
                connect(this.host, this.port);

            ChannelFuture channelFuture = this.socketChannel.writeAndFlush(maplePacket).sync();
            log.info("报文发送{}", channelFuture.isSuccess() ? "成功" : "失败");

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
